The invisible messenger of Android. Intercept system-wide events, respond to app signals, and build reactive applications with one of Android's most powerful components.
A Broadcast Receiver is an Android component that allows your application to subscribe to system-wide or app-level events. Think of it as a radio tuner — it listens on specific channels and reacts when a signal arrives.
When something significant happens — the battery gets low, Wi-Fi connects, an SMS arrives, or even when your own app wants to signal other parts of itself — Android sends out a broadcast Intent. Any registered receiver tuned to that frequency wakes up and responds.
Receivers work even when your app isn't in the foreground, making them ideal for background reactions to real-world events — without keeping your app alive unnecessarily.
Something triggers a broadcast — the system boots, a network changes, a call comes in, or your app fires a custom event. This is the spark that starts the chain.
TRIGGERThe broadcaster creates an Intent object with an action string (like ACTION_BATTERY_LOW) and optional extras — data that travels with the signal.
The OS checks all registered receivers — both those declared in AndroidManifest.xml and those registered dynamically — to find matching IntentFilters.
Your receiver's onReceive(Context, Intent) method fires on the main thread. You have a strict 10-second window to complete work — for longer tasks, delegate to a Service or WorkManager.
Delivered to all receivers asynchronously and in an undefined order. Most efficient type — all receivers get the broadcast roughly simultaneously with no defined priority.
sendBroadcast()Delivered to receivers one at a time based on priority. Each receiver can propagate or abort the broadcast before the next receiver sees it. Useful for interception patterns.
sendOrderedBroadcast()Stays within your app's process using LocalBroadcastManager. More efficient and secure — data never leaves your app. Ideal for in-app communication.
Broadcasts that stay in the system after being sent. When a new receiver registered, it immediately receives the last sticky broadcast for that action. Replaced by explicit data stores like SharedPreferences or ViewModel.
sendStickyBroadcast()// Step 1: Create your BroadcastReceiver class class BatteryReceiver : BroadcastReceiver() { @Override override fun onReceive(context: Context, intent: Intent) { when (intent.action) { Intent.ACTION_BATTERY_LOW -> { // Battery is critically low — take action showLowBatteryNotification(context) } Intent.ACTION_BATTERY_OKAY -> { // Battery recovered — clear alerts clearNotification(context) } } } private fun showLowBatteryNotification(context: Context) { // Build and show notification... val msg = "⚡ Battery is critically low!" Toast.makeText(context, msg, Toast.LENGTH_LONG).show() } }
<!-- AndroidManifest.xml — Static Registration --> <manifest> <application> <!-- Declare the receiver component --> <receiver android:name=".BatteryReceiver" android:exported="false"> <intent-filter> <!-- Listen for battery events --> <action android:name="android.intent.action.BATTERY_LOW" /> <action android:name="android.intent.action.BATTERY_OKAY" /> </intent-filter> </receiver> </application> </manifest> <!-- Note: Many implicit broadcasts can NO LONGER be registered --> <!-- in the manifest as of Android 8.0 (API 26) — use Context --> <!-- registration or WorkManager for background tasks instead. -->
class MainActivity : AppCompatActivity() { private lateinit var networkReceiver: NetworkChangeReceiver private lateinit var filter: IntentFilter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Create receiver and intent filter networkReceiver = NetworkChangeReceiver() filter = IntentFilter().apply { addAction(ConnectivityManager.CONNECTIVITY_ACTION) } } override fun onResume() { super.onResume() // ✅ Register when active registerReceiver(networkReceiver, filter) } override fun onPause() { super.onPause() // ✅ Always unregister to prevent memory leaks! unregisterReceiver(networkReceiver) } }
ACTION_BATTERY_LOW
Fired when device battery drops to a critically low level. Useful for pausing sync operations.
CONNECTIVITY_ACTION
Network state changed — Wi-Fi connected, disconnected, or mobile data toggled.
ACTION_BOOT_COMPLETED
Device has finished booting. Start persistent services or schedule jobs after reboot.
ACTION_TIME_CHANGED
User manually changed device time or date. Useful for calendar and scheduling apps.
ACTION_PACKAGE_ADDED
A new app was installed on the device. Used by launchers and app managers.
ACTION_POWER_CONNECTED
Device connected to a charger. Ideal trigger for heavy background sync operations.
Unlike Activities, a Broadcast Receiver has an extremely simple lifecycle — it exists only for the duration of onReceive().
System or app fires the Intent
OS finds registered receivers with matching filter
Your code runs — max 10 seconds on main thread
Method returns, receiver is inactive again
onReceive(). The system can kill your process once the method returns. For async work, use WorkManager, JobScheduler, or start a foreground Service.
Unregister in onPause() or onDestroy() to prevent memory leaks and ghost receivers that silently drain resources.
When communicating within your app, local broadcasts are faster, safer, and data stays within your process. Prefer it over global broadcasts.
Network calls, database writes, and file I/O have no place in a receiver. Delegate to WorkManager or a Service immediately and return fast.
Android 8.0+ restricts background implicit broadcasts. Many system actions can no longer wake static receivers — use dynamic registration or WorkManager constraints.
If your receiver isn't meant to receive broadcasts from other apps, set android:exported="false" to prevent unintended external access.
Protect your custom broadcasts with a permission string to ensure only authorized apps can send or receive your proprietary signals.